package com.zlm;

import cn.cangfei.massage.entity.*;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import cn.cangfei.massage.MassageApplication;
import cn.cangfei.massage.constant.RedisConstants;
import cn.cangfei.massage.dto.MassageDoctorDTO;
import cn.cangfei.massage.dto.TimePeriodEnum;
import cn.cangfei.massage.dto.VisitPlanResiduesDTO;
import cn.cangfei.massage.dto.param.VisitAppointmentParam;
import cn.cangfei.massage.dto.update.VisitAppointmentDto;
import cn.cangfei.massage.mapper.MassageDoctorMapper;
import cn.cangfei.massage.mapper.VisitAppointmentMapper;
import cn.cangfei.massage.mapper.VisitPlanMapper;
import cn.cangfei.massage.service.IMassageDoctorService;
import cn.cangfei.massage.service.IPowerAccountService;
import cn.cangfei.massage.service.IUserBasicInfoService;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@SpringBootTest(classes = MassageApplication.class)
@RunWith(SpringRunner.class)
public class test {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private MassageDoctorMapper doctorInfoMapper;

    @Resource
    private VisitPlanMapper planMapper;

    @Resource
    private VisitAppointmentMapper appointmentMapper;

    @Resource
    private IUserBasicInfoService userBasicInfoService;

    @Resource
    private IPowerAccountService powerAccountService;

    @Resource
    private IMassageDoctorService doctorService;

    @Resource
    private RedissonClient redissonClient;

    // 提前一个小时上架
    // 上架信息：师傅信息  key: doctorId value:就是那个json对象
    // 预约信息  hash存储  key: hospId+day  hashKey: doctorId value:就是那个json对象
    // 出诊信息  根据doctorId和massageId  那么也是 hash存储  key: hospId  hashKey: doctorId  value:就是那个json对象
    //
    // 有人预约了后， key: hospId+day hashKey: userId hashValue:就是后面的json对象 目前思路 预约成功的话 记录状态，timePeriod,duan(去出诊信息里面看是否以timePeriod里面一样),

    // 判断是否预约三次的话 遍历 hospId+day 根据userId 获取到所有的 hashValue,然后去看里面的状态

    // 取消预约，就是修改状态，并且还要修改预约信息 TODO 有点恼火

    @Test
    public void shangChuan() {

        Long massageId = 1000L;
        List<MassageDoctorDTO> doctorDTOS = doctorInfoMapper.getInfoByMassageId(massageId);


        // 1.上架信息：师傅信息  key: doctorId value:就是那个json对象
        // 批量保存到redis
        for (MassageDoctorDTO doctorDTO : doctorDTOS) {
            // doctorDTO转为JSON字符串
            String jsonString = JSON.toJSONString(doctorDTO);

            stringRedisTemplate.opsForValue().set(RedisConstants.DOCTOR_INFO_PREFIX + doctorDTO.getId(), jsonString);
        }

        // 获取当天出诊计划
        VisitPlanExample example = new VisitPlanExample();

        Long doctorId = 1000L;
        example.createCriteria()
                .andDoctorIdEqualTo(doctorId)
                .andMassageIdEqualTo(massageId);
        List<VisitPlan> list = planMapper.selectByExample(example);

        // 批量保存到redis
        for (VisitPlan visitPlan : list) {
            // visitPlan转为JSON字符串
            String jsonString = JSON.toJSONString(visitPlan);

            stringRedisTemplate.opsForValue().set(RedisConstants.PLAN_INFO_PREFIX + visitPlan.getDoctorId(), jsonString);
        }

        // 将用户信息存入redis
        // key 就是userId value就是userBasicInfo
        List<UserBasicInfo> userBasicInfos = userBasicInfoService.listAll();
        for (UserBasicInfo userBasicInfo : userBasicInfos) {
            String jsonString = JSON.toJSONString(userBasicInfo);
            stringRedisTemplate.opsForValue().set(RedisConstants.USER_INFO_PREFIX + userBasicInfo.getId(), jsonString);
        }
    }

    /**
     * 获取师傅出诊信息
     *
     * @param massageId 推拿馆编号
     * @param doctorId   师傅编号
     * @param week       星期
     * @return 师傅出诊信息
     */
    public List<VisitPlanResiduesDTO> getDoctorPlanByDate(Long massageId, Long doctorId, String week, String date) {

        // 从redis中获取
        String planInfo = stringRedisTemplate.opsForValue().get(RedisConstants.PLAN_INFO_PREFIX + doctorId);
        VisitPlan visitPlan = JSONUtil.toBean(planInfo, VisitPlan.class);
        if (visitPlan == null) {
            return null;
        }
        List<VisitPlan> list = new ArrayList<>();
        list.add(visitPlan);

        if (CollUtil.isEmpty(list)) {
            return null;
        }
        List<VisitPlan> collect = list.stream().filter((item) -> {
            String day = item.getDay();
            String[] split = day.split(",");
            // 也就是这天要上班的
            // 这个人所有的班的星期
            List<String> list1 = Arrays.asList(split);
            // week表示今天星期几
            return list1.contains(week);
        }).collect(Collectors.toList());


        // 每一个计划，我们根据doctorId来进行关联
        if (CollUtil.isEmpty(collect)) {
            return null;
        }

        return collect.stream()
                .map((item) -> {
                    // date表示查询的是哪一天的
                    return covertToResidues(date, item);
                })
                .collect(Collectors.toList());
    }

    private VisitPlanResiduesDTO covertToResidues(String day, VisitPlan plan) {

        VisitPlanResiduesDTO dto = new VisitPlanResiduesDTO();
        BeanUtils.copyProperties(plan, dto);

        SimpleDateFormat dateFormat1 = new SimpleDateFormat("yyyy-MM-dd");
        List<Integer> residueList = new ArrayList<>();


        Date date1 = new Date();
        // 表示今天的年-月-日
        String format1 = dateFormat1.format(date1);


        // 将

        // 获取到今天预约根据师傅id的信息
        Long doctorId1 = plan.getDoctorId();
        // 从redis中获取
        // key 是
        String key = RedisConstants.USER_VISIT_PREFIX + day;
        // 因为是hash存储 我们要得到所有的hashValue
        // 这里面的key是userId
        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(key);
        // 这里面的key是userId
        Collection<Object> values = entries.values();
        List<VisitAppointment> visitAppointmentList = new ArrayList<>();
        for (Object value : values) {
            // 将value转为VisitAppointment
            VisitAppointment visitAppointment = JSONUtil.toBean((String) value, VisitAppointment.class);
            if (visitAppointment.getDoctorId().equals(doctorId1)) {
                visitAppointmentList.add(visitAppointment);
            }
        }
        // List<VisitAppointment> visitAppointmentList = appointmentService.getAppointmentListByToDayAndDoctorId(day, doctorId1);
        if (visitAppointmentList.size() == 0) {
            dto.setAppointments(new ArrayList<>());
        } else {
            List<VisitAppointmentDto> collect = visitAppointmentList.stream()
                    .filter(item -> {
                        return item.getStatus() == 0;
                    }).map(item -> {
                        VisitAppointmentDto visitAppointmentDto = new VisitAppointmentDto();
                        BeanUtils.copyProperties(item, visitAppointmentDto);
                        Long userId = item.getUserId();
                        // 从redis中获取

                        String jsonString = stringRedisTemplate.opsForValue().get(RedisConstants.USER_INFO_PREFIX + userId);
                        UserBasicInfo userBasicInfo = JSONUtil.toBean(jsonString, UserBasicInfo.class);

                        String child = userBasicInfo.getChild();
                        String openId = userBasicInfo.getOpenId();
                        visitAppointmentDto.setChildName(child);
                        visitAppointmentDto.setOpenId(openId);
                        return visitAppointmentDto;
                    }).sorted((item1, item2) -> {
                        return item1.getTimePeriod() - item2.getTimePeriod();
                    })
                    .collect(Collectors.toList());
            dto.setAppointments(collect);
        }

        Long doctorId = plan.getDoctorId();

        // 现在我还要获取时间段
        // 从redis中获取
        String planInfo = stringRedisTemplate.opsForValue().get(RedisConstants.PLAN_INFO_PREFIX + doctorId);
        VisitPlan visitPlan = JSONUtil.toBean(planInfo, VisitPlan.class);
        String shijianduan = visitPlan.getDuan();
        //String shijianduan = planMapper.getShiJianDuan(doctorId);
        String[] split = shijianduan.split(",");
        // 进行比较如果当前的已经过期了就要进行记录下标后面好重置
        int size = split.length;

        // 判断比传过来的data大如果大的话就直接设置为0;
        // 大于的话
        if (format1.compareTo(day) > 0) {
            for (int i = 0; i < size; i++) {
                // 过了当天全部返回-1
                residueList.add(-1);
            }

            dto.setResidues(residueList);
            return dto;
        }


        // 获取当前时间
        SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm");
        Date date = new Date();

        // TODO 获取当前师傅的就诊情况
        // 获取到当前的时间 就是09:00这样的样式
        String format = dateFormat.format(date);
        // 然后要和获取到的时间段进行比对
        // 将每一个时间段进行比较


        List<Integer> arr = new ArrayList<>();

        // 注意这里有问题，我们必须只有挂号今天的话才能判断日期
        if (format1.compareTo(day) == 0) {
            for (int i = 0; i < size; i++) {
                //
                String meigeshijianduan = split[i];

                // 取后面那一个
                String[] split1 = meigeshijianduan.split("-");
                String xuyaobijiaodeshijian = split1[1];
                // 当前时间只要大于等于了这个时间段最后那个时间就表示过期了
                if (format.compareTo(xuyaobijiaodeshijian) >= 0) {
                    // 这个要进行 + 1因为下面的timePeriod是从1开始的，最后全部-1进行统一处理就可以了
                    // 这里面就是过了时间段的
                    arr.add(i + 1);
                }


                // 就看当前的时间是不是大于等于这个时间段的截止时间如果是的话就记录进行后面好做统一的处理
            }
        }

        // 如果是往后的几天就不能进行时间判断，只判断这个时间段是否可以被……买了


        // 先根据plan的doctorId查询出appointment里面的这个人timePeriod
        // 然后将这个下标-1那个集合的位置变为0

        // day表示当时的获取时间


        // 这个是查询到挂号了的(没有取消的)
        // 这个就是需要进行检票的一个机制就可以了
        // 这个就是买过的
        // 这里还要判断状态必须是0,1因为2就表示取消了的
        List<Integer> listTimePeriod = planMapper.getListTimePeriod(day, plan.getDoctorId());
        System.out.println("预约了的:" + listTimePeriod);
        // 判断一下是不是全部预约了
        /*if (listTimePeriod.size() == size) {
            for (int i = 0; i < size; i++) {
                // 现就就是这一天全部被人预约了的话就返回-2
                residueList.add(-2);
//                residueList.add(0);
            }
            dto.setResidues(residueList);
            return dto;
        }*/

        List<Integer> zuiZhong = new ArrayList<>();
        if (arr.size() > 0) {
            arr.forEach((item) -> {
                zuiZhong.add(item - 1);
            });
        }
        if (listTimePeriod.size() > 0) {
            listTimePeriod.forEach(item -> {
                zuiZhong.add(item - 1);
            });
        }

//        Integer timeperiod = appointmentService.getTimePeriodByDoctorId(doctorId);

        System.out.println("过了时间段的：" + arr);
        TimePeriodEnum timePeriod = (plan.getTime().equals(TimePeriodEnum.AM.getTime())) ? TimePeriodEnum.AM : TimePeriodEnum.PM;
//        System.out.println(zuizhong);
        // 这里先用集合进实现
        Integer[] zuizhongInteger = new Integer[size];
        for (int i = 0; i < size; i++) {

            // 有位置就是5
//            zuizhongInteger[i] = MAX_OF_PATIENTS - appointmentService.countByPlanId(plan.getId(), plan.getTime());
            zuizhongInteger[i] = 5;
            // 是否被预约
            if (listTimePeriod.size() > 0) {
                if (listTimePeriod.contains(i + 1)) {
                    zuizhongInteger[i] = -2;
                }
            }
            // 时间段
            if (arr != null) {
                if (arr.contains(i + 1)) {
                    zuizhongInteger[i] = -1;
                }
            }


        }

        for (int i = 0; i < size; i++) {
            residueList.add(zuizhongInteger[i]);
        }

        // 设置剩余号数
        dto.setResidues(residueList);

        return dto;
    }


    @Transactional
    public boolean insert(String name, VisitAppointmentParam param, Integer countMax, Long hospId) {
//        name = "o_fcm493kgxyX25gy7U3mIbH5hMg";
        // 使用redisson上锁
        RLock lock = redissonClient.getLock("yuyue");
        //获取锁
        boolean flag = lock.tryLock();
        if (!flag) {
            throw new RuntimeException("预约失败请刷新");
        }
        try {
            // 根据openId查询userId
            UserBasicInfo user1 = userBasicInfoService.getUserByOpenId(name);
            Long user1Id = user1.getId();


            // TODO 使用redis
            String key = RedisConstants.USER_VISIT_PREFIX + hospId + ":" + param.getDay();
            // hashKey就是userId
            String hashKey = user1Id.toString();
            // 获取到这个人的预约信息全部
            Object o = stringRedisTemplate.opsForHash().get(key, hashKey);
            List<VisitAppointment> bean = JSONUtil.toBean((String) o, List.class);
            // 遍历看day和status(0,1)
            int count = 0;
            if (bean != null) {
                for (VisitAppointment visitAppointment : bean) {
                    if (Objects.equals(visitAppointment.getDay(), param.getDay()) && (Objects.equals(visitAppointment.getStatus(), 0) || Objects.equals(visitAppointment.getStatus(), 1))) {
                        count++;
                    }
                }
            }
            // 先去查看这个这一天预约的次数有没有超过count次
            LambdaQueryWrapper<VisitAppointment> wrapper1 = new LambdaQueryWrapper<>();
            wrapper1.eq(VisitAppointment::getDay, param.getDay());
            // 只要你是预约了并且没有取消的还没有迟到的就不能预约了
            wrapper1.in(VisitAppointment::getStatus, 0, 1);
            wrapper1.eq(VisitAppointment::getUserId, user1Id);
            wrapper1.eq(VisitAppointment::getHospId, hospId);
            Integer appointMentCount = appointmentMapper.selectCount(wrapper1);
            if (Objects.equals(appointMentCount, countMax)) {
                throw new RuntimeException("今天的预约次数已经超过了" + countMax + "次");
            }

            // TODO 查询redis
            // 先去查询这个时间段有没有被预约
            LambdaQueryWrapper<VisitAppointment> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(VisitAppointment::getTimePeriod, param.getTimePeriod());
            wrapper.eq(VisitAppointment::getDay, param.getDay());
            wrapper.eq(VisitAppointment::getDoctorId, param.getDoctorId());

            /*
             wrapper.and(i -> i.in(VisitAppointment::getStatus, arrayList));
             */

            // wrapper.in(VisitAppointment::getStatus, arrayList);

            List<VisitAppointment> list = appointmentMapper.selectList(wrapper);
            List<VisitAppointment> collect = new ArrayList<>();
            if (list != null && list.size() > 0) {
                // 进行过滤处理
                collect = list.stream().filter(visitAppointment -> visitAppointment.getStatus() == 0 || visitAppointment.getStatus() == 1).collect(Collectors.toList());

            }
            // collect就表示看看有没有被预约的
            if (collect.size() > 0) {
                // 表示无法预约
                throw new RuntimeException("已经被预约了");
            }


            Optional<PowerAccount> powerAccount = powerAccountService.getByName(name);
            if (!powerAccount.isPresent()) {
                return false;
            }

            PowerAccount account = powerAccount.get();

            // 根据account的openId获取到userId
            String accountName = account.getName();
            UserBasicInfo user = userBasicInfoService.getUserByOpenId(accountName);


            // 根据师傅id获取到师傅的信息看师傅是否存在
            Long doctor_id = param.getDoctorId();
            boolean counted = doctorService.count(doctor_id);
            if (!counted) {
                throw new RuntimeException("师傅不存在");
            }

            VisitAppointment appointment = new VisitAppointment();
            appointment.setUserId(user.getId());
            BeanUtils.copyProperties(param, appointment);
            // 获取当时的时间段
            // 从redis中获取
            String planInfo = stringRedisTemplate.opsForValue().get(RedisConstants.PLAN_INFO_PREFIX + param.getDoctorId());
            VisitPlan visitPlan = JSONUtil.toBean(planInfo, VisitPlan.class);

            // String duan = visitPlanService.getDuan(param.getDoctorId());
            String duan = visitPlan.getDuan();
            // 获取属于哪个时间段
            // TODO
            try {
                duan = duan.split(",")[param.getTimePeriod() - 1];
            } catch (Exception e) {
                throw new RuntimeException("时间段不存在");
            }

            // 先从redis中获取，然后加入进去
            Object o1 = stringRedisTemplate.opsForHash().get(RedisConstants.USER_VISIT_PREFIX + hospId + ":" + param.getDay(), user1Id.toString());
            List<VisitAppointment> bean1 = JSONUtil.toBean((String) o1, List.class);
            if (bean1 == null) {
                bean1 = new ArrayList<>();
            }


            appointment.setTime(duan);
            appointment.setDay(param.getDay());
            appointment.setTixing(0L);
            appointment.setGmtCreate(new Date());
            appointment.setGmtModified(new Date());
            appointment.setStatus(0);
            bean1.add(appointment);
            //int selective = appointmentMapper.insert(appointment);
            // TODO 使用redis
            stringRedisTemplate.opsForHash().put(RedisConstants.USER_VISIT_PREFIX + hospId + ":" + param.getDay(), user1Id.toString(), JSONUtil.toJsonStr(bean1));
            return true;
        } finally {
            // 因为开启了多线程的
            if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                // 解锁
                lock.unlock();
            }
        }

    }

}
